package com.blog.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.blog.constant.MsgConstant;
import com.blog.constant.RedisConstant;
import com.blog.mapper.ArticleMapper;
import com.blog.mapper.CategoryMapper;
import com.blog.pojo.dto.CategoryDTO;
import com.blog.pojo.entity.Article;
import com.blog.pojo.entity.Category;
import com.blog.service.CategoryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)  // 添加事务回滚注解，当遇到错误时，会回滚事务，确保数据的完整
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ArticleMapper articleMapper;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     * 分页查询
     * @param pageNum
     * @param pageSize
     * @param categoryName
     * @param categoryAlias
     * @return
     */
    @Override
    @Transactional(readOnly = true) // 添加只读事务管理，不会进行数据锁，可以解放部分资源
    public IPage<Category> pageQuery(Integer pageNum, Integer pageSize, String categoryName, String categoryAlias) {
        try {
            // 1. 创建分页对象
            Page<Category> page = new Page<>(pageNum, pageSize);

            // 2. 构建查询条件
            LambdaQueryWrapper<Category> wrapper = new LambdaQueryWrapper<>();

            // 3. 添加分类名称的模糊查询条件
            if (StrUtil.isNotBlank(categoryName)) {
                wrapper.like(Category::getCategoryName, categoryName);
            }

            // 4. 添加分类别名的模糊查询条件
            if (StrUtil.isNotBlank(categoryAlias)) {
                wrapper.like(Category::getCategoryAlias, categoryAlias);
            }
            // 5. 添加排序条件
            wrapper.orderByDesc(Category::getCreateTime);

            // 6. 执行分页查询
            return page(page, wrapper);
        } catch (Exception e) {
            log.info("分页查询分类失败: ", e);
            throw new RuntimeException(MsgConstant.CATEGORY_SELECT_ERROR);
        }
    }

    /**
     * 获取全部分类信息
     * @return
     */
    @Override
    public List<Category> getCategoryAll() {
        // 1. 从Redis中擦尝试获取缓存
        String categoryJson = stringRedisTemplate.opsForValue().get(RedisConstant.CATEGORY_REDIS_KEY);
        // 2. 判断缓存的数据是否存在
        if (StrUtil.isNotBlank(categoryJson)) {
            log.info("从Redis中获取分类数据成功");
            // 2.1 数据存在，转换成实体对象
            return JSONUtil.toList(categoryJson, Category.class);
        }
        // 3. 缓存数据不存在，创建 wrapper 对象，指定查询到的数据按照id排序
        LambdaQueryWrapper<Category> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByDesc(Category::getId);
        // 3.1. 进行查询
        List<Category> categoryList = new ArrayList<>();
        try {
            categoryList = this.list(wrapper);
        } catch (Exception e) {
            log.info("分类查询全部数据失败: ", e);
            throw new RuntimeException(MsgConstant.CATEGORY_SELECT_ERROR);
        }
        //4. 判断数据是否存在
        if (categoryList.size() == 0) {
            // 4.1 不存在，返回错误
            throw new RuntimeException(MsgConstant.CATEGORY_NO_EXISTS);
        }
        // 4.2 数据存在，存入Redis缓存
        stringRedisTemplate.opsForValue().set(
                RedisConstant.CATEGORY_REDIS_KEY, JSONUtil.toJsonStr(categoryList),
                 RedisConstant.CATEGORY_REDIS_TTL, TimeUnit.MINUTES);

        return categoryList;
    }

    /**
     * 根据id获取分类名称
     * @param id
     * @return
     */
    @Override
    public String getCategoryName(Long id) {
        // 1. 先检查该分类是否存在于数据库中
        Category category = this.getById(id);
        if (category == null) {
            throw new RuntimeException(MsgConstant.CATEGORY_NO_EXISTS);
        }

        return category.getCategoryName();
    }

    /**
     * 单条分类数据添加
     * @param categoryDTO
     */
    @Override
    public void savaOne(CategoryDTO categoryDTO) {
        // 1. 判断分类名称是否已存在
        LambdaQueryWrapper<Category> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Category::getCategoryName, categoryDTO.getCategoryName());
        if (this.count(wrapper) > 0) {
            throw new RuntimeException(MsgConstant.CATEGORY_NAME_EXISTS);
        }

        // 2. 使用工具类复制DTO中的字段到category实体中，用于sava()，因为plus绑定的泛型是category，所以只能使用category进行sava
        Category category = BeanUtil.copyProperties(categoryDTO, Category.class);

        // 3. 设置创建时间和更新时间
        category.insertTime();

        // 4. 保存分类
        try {
            this.save(category);
            // 4.1 删除redis缓存
            deleteRedisCache();
        } catch (Exception e) {
            log.error("保存分类失败：", e);
            throw new RuntimeException(MsgConstant.CATEGORY_ADD_ERROR);
        }
    }

    /**
     * 删除单条分类
     * @param id
     */
    @Override
    public void deleteOne(Long id) {
        // 1. 先判断该分类在数据库中是否存在
        LambdaQueryWrapper<Category> categoryWrapper = new LambdaQueryWrapper<>();
        categoryWrapper.eq(Category::getId, id);
        if (this.count(categoryWrapper) == 0) {
            throw new RuntimeException(MsgConstant.CATEGORY_NO_EXISTS);
        }

        // 2. 检查该分类下还有没有文章，没有才可以进行删除
        LambdaQueryWrapper<Article> articleWrapper = new LambdaQueryWrapper<>();
        articleWrapper.eq(Article::getCategoryId, id);
        if (articleMapper.selectCount(articleWrapper) > 0) {
            throw new RuntimeException(MsgConstant.CATEGORY_HAS_ARTICLE);
        }

        // 3. 全部校验通过。进行删除分类
        try {
            this.removeById(id);
            // 4. 删除redis缓存
            deleteRedisCache();
        } catch (Exception e) {
            log.error("删除分类失败：", e);
            throw new RuntimeException(MsgConstant.CATEGORY_DELETE_ERROR);
        }

    }

    /**
     * 批量删除分类
     * @param ids 要删除的分类ID列表
     */
    @Override
    public void deleteBatch(List<Long> ids) {
        // 1. 检查要删除的分类是否都存在
        LambdaQueryWrapper<Category> categoryWrapper = new LambdaQueryWrapper<>();
        categoryWrapper.in(Category::getId, ids);
        long count = this.count(categoryWrapper);
        if (count != ids.size()) {
            throw new RuntimeException(MsgConstant.CATEGORY_NO_EXISTS);
        }

        // 2. 检查这些分类下是否有文章
        LambdaQueryWrapper<Article> articleWrapper = new LambdaQueryWrapper<>();
        articleWrapper.in(Article::getCategoryId, ids);
        if (articleMapper.selectCount(articleWrapper) > 0) {
            throw new RuntimeException(MsgConstant.CATEGORY_HAS_ARTICLE);
        }

        // 3. 批量删除分类
        try {
            this.removeBatchByIds(ids);
            // 4. 删除redis缓存
            this.deleteRedisCache();
        } catch (Exception e) {
            log.error("批量删除分类失败：", e);
            throw new RuntimeException(MsgConstant.CATEGORY_DELETE_ERROR);
        }
    }

    /**
     * 修改分类
     * @param categoryDTO
     */
    @Override
    public void updateOne(CategoryDTO categoryDTO) {
        // 1. 先将DTO转换成category实体
        Category category = BeanUtil.copyProperties(categoryDTO, Category.class);

        // 2. 可以判断一下这个分类存不存在
        LambdaQueryWrapper<Category> categoryWrapper = new LambdaQueryWrapper<>();
        categoryWrapper.eq(Category::getId, category.getId());
        if (this.count(categoryWrapper) == 0) {
            throw new RuntimeException(MsgConstant.CATEGORY_NO_EXISTS);
        }

        // 3. 进行更新
        try {
            this.updateById(category);
            // 4. 删除redis缓存
            this.deleteRedisCache();
        } catch (Exception e) {
            log.error("更新分类失败：", e);
            throw new RuntimeException(MsgConstant.CATEGORY_UPDATE_ERROR);
        }
    }

    /**
     * 删除Redis的分类缓存
     */
    private void deleteRedisCache() {
        // 1. 删除Redis缓存
        stringRedisTemplate.delete(RedisConstant.CATEGORY_REDIS_KEY);
        // 2. 创建线程池，休眠一段时间，异步删除Redis缓存
        threadPoolTaskExecutor.submit(() -> {
            try {
                Thread.sleep(500);
                stringRedisTemplate.delete(RedisConstant.CATEGORY_REDIS_KEY);
            } catch (InterruptedException e) {
                log.error("删除Redis缓存失败：", e);
            }
        });
    }


}

